﻿using System;
using System.Collections.Generic;
using KxParser.Lexer.Common;
using System.Text.RegularExpressions;

namespace KxParser.Parser.LR0
{
    class ParsingProcedure
    {
        //private State _currentState;
        private Queue<string> _input = new Queue<string>();
        private Stack<int> _output = new Stack<int>();
        private Stack<string > _stack = new Stack<string>();
        private string _next;
        private State[] _states;
        private ParserTable _parserTable;

        private readonly Token[] _inputTokens;

        private bool IsError;

        private string _terminal;

        public ParsingProcedure(ParserTable parserTable, State[] states, Token[] inputTokens)
        {
            _parserTable = parserTable;
            _states = states;

            _inputTokens = inputTokens;

			//
			List<string> tokenNames = new List<string>();
			string tokenName = string.Empty;

			for (int i = 0; i < inputTokens.Length; i++)
			{
				_input.Enqueue(inputTokens[i].FullName);
			}

			//foreach (var s in inputTokens)
			//{
			//    _input.Enqueue(s.Name);
			//}

            _stack.Push("1");
        }

        public bool Parse()
        {
            TableAction tableAction;

            bool reduce;

            do
            {
                var currentStateId = GetLastStateFromStack();

                tableAction = _parserTable.GetAction(currentStateId, Global.Ender);
                if (tableAction != null && tableAction.Action == TableAction.EAction.Reduce)
                {
                    reduce = true;
                }
                else
                {
                    reduce = false;

					string ss = _input.Dequeue();
					string[] terminals = Regex.Split(ss, Global.TokenNameSeparator);

					// Find any matching action
					foreach (string terminal in terminals)
					{
						_terminal = terminal;
						tableAction = _parserTable.GetAction(currentStateId, terminal);
						if (tableAction != null)
							break;
					}

					/*
					_terminal = _input.Dequeue();
                    tableAction = _parserTable.GetAction(currentStateId, _terminal);
					 */
                }

                if (tableAction == null)
                {
                    IsError = true;
                    return false;
                }

				Console.WriteLine("Action = " + tableAction.Action + ", data = " + tableAction.Data);
                switch (tableAction.Action)
                {
                    case TableAction.EAction.Accept:
                        break;

                    case TableAction.EAction.Shift:
                        _stack.Push(tableAction.Data.ToString());
                        break;
                    case TableAction.EAction.Reduce:
                        _output.Push(tableAction.Data);

                        var rule = Global.ProductionItems[tableAction.Data];

                        var sp = rule.Values.Count;
                        for (var i = 0; i < sp; i++)
                        {
                            _stack.Pop();
                        }

                        var a = _parserTable.GetAction(GetLastStateFromStack(), rule.Name);

                        if(a!=null)
                            _stack.Push(a.Data.ToString());

                        break;
                }
            } while (tableAction.Action != TableAction.EAction.Accept && tableAction.Action != TableAction.EAction.Error);

            IsError = ! (tableAction.Action == TableAction.EAction.Accept);
            return !IsError;
        }

        private int GetLastStateFromStack()
        {
            int lastStateId;
            var tempStack = new Stack<string>();

            try
            {
                while (!int.TryParse(_stack.Peek(), out lastStateId))
                {
                    tempStack.Push(_stack.Pop());
                }

                while (tempStack.Count > 0)
                {
                    _stack.Push(tempStack.Pop());
                }

                return lastStateId;
            }catch(Exception exception)
            {
                throw new Exception("Cannot get last state! " + exception.Message + ". " + exception.StackTrace);
            }
        }

        public Token GetErrorToken()
        {
            if (!IsError)
                return null;

            var idx = _inputTokens.Length - ( _input.Count + 1 );
            return _inputTokens[idx];
        }
    }
}
